package org.luxor.cloud.gateway.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.extern.slf4j.Slf4j;
import org.luxor.cloud.gateway.component.route.RouteTransform;
import org.luxor.cloud.gateway.entity.GatewayRouteEntity;
import org.luxor.cloud.gateway.mapper.GatewayRouteMapper;
import org.luxor.cloud.gateway.service.DynamicRouteService;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 网关动态路由定义
 *
 * @author Mr.Yan  @date: 2020/10/30
 */
@Slf4j
@Service
public class DynamicRouteServiceImpl implements DynamicRouteService, ApplicationEventPublisherAware {

    private Cache<String, Optional<GatewayRouteEntity>> gatewayRouteEntityCache = CacheBuilder.newBuilder().expireAfterAccess(55, TimeUnit.SECONDS).build();

    @Resource
    private GatewayRouteMapper gatewayRouteMapper;
    @Resource
    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher publisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    /**
     * 发布 (根据ID批量发布)
     */
    @Override
    public boolean deployByIds(Collection<Long> routeIds) {
        if (CollectionUtils.isEmpty(routeIds)) {
            return true;
        }

        // 更新状态为'上线(1)'状态
        LambdaUpdateWrapper<GatewayRouteEntity> updateWrapper = Wrappers.<GatewayRouteEntity>lambdaUpdate()
                .in(GatewayRouteEntity::getRouteId, routeIds);
        GatewayRouteEntity updateEntity = new GatewayRouteEntity();
        updateEntity.setStatus((byte) 1);
        Integer result = gatewayRouteMapper.update(updateEntity, updateWrapper);

        this.gatewayRouteEntityCache.invalidateAll();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return SqlHelper.retBool(result);
    }

    /**
     * 下线 (根据ID批量下线)
     */
    @Override
    public boolean downLineByIds(Collection<Long> routeIds) {
        if (CollectionUtils.isEmpty(routeIds)) {
            return true;
        }

        // 更新状态为'已下线(2)'状态
        LambdaUpdateWrapper<GatewayRouteEntity> updateWrapper = Wrappers.<GatewayRouteEntity>lambdaUpdate()
                .in(GatewayRouteEntity::getRouteId, routeIds);
        GatewayRouteEntity updateEntity = new GatewayRouteEntity();
        updateEntity.setStatus((byte) 2);
        Integer result = gatewayRouteMapper.update(updateEntity, updateWrapper);

        // 移除已下线路由
        routeIds.forEach(routeId -> routeDefinitionWriter.delete(Mono.just(String.valueOf(routeId))));

        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return SqlHelper.retBool(result);
    }

    /**
     * 新增
     */
    @Override
    public boolean save(GatewayRouteEntity entity) {
        if (uniqueCondition(entity)) {
            log.warn("新增失败(违反唯一约束). {}", entity);
            return false;
        }
        entity.setStatus((byte) 0);
        Integer result = gatewayRouteMapper.insert(entity);
        return SqlHelper.retBool(result);
    }

    /**
     * 删除
     */
    @Override
    public boolean removeByIds(Collection<Long> idList) {
        if (notDeleteCondition(idList)) {
            log.warn("删除失败(不能有'已发布'状态的记录). {}", idList);
            return false;
        }
        Integer result = gatewayRouteMapper.deleteBatchIds(idList);
        return SqlHelper.retBool(result);
    }

    /**
     * 修改
     */
    @Override
    public boolean updateById(GatewayRouteEntity entity) {
        if (uniqueCondition(entity)) {
            log.warn("修改失败(违反唯一约束). {}", entity);
            return false;
        }
        // 强制失效
        entity.setStatus((byte) 0);
        Integer result = gatewayRouteMapper.updateById(entity);

        return SqlHelper.retBool(result);
    }

    /**
     * 查看
     */
    @Override
    public GatewayRouteEntity getById(Long routeId) {
        return gatewayRouteMapper.selectById(routeId);
    }

    /**
     * 查看
     */
    @Override
    public Optional<GatewayRouteEntity> getOne(String routePath, String routeVersion, String routeMethod) {
        String uniqueKey = String.format("%s_%s_%s", routePath.hashCode(), routeVersion.hashCode(), routeMethod);
        try {
            return gatewayRouteEntityCache.get(uniqueKey, () -> {
                LambdaQueryWrapper<GatewayRouteEntity> queryWrapper = Wrappers.<GatewayRouteEntity>lambdaQuery()
                        .eq(GatewayRouteEntity::getStatus, 1)
                        .eq(GatewayRouteEntity::getRoutePath, routePath)
                        .eq(GatewayRouteEntity::getRouteVersion, routeVersion)
                        .eq(GatewayRouteEntity::getRouteMethod, routeMethod);
                GatewayRouteEntity entity = gatewayRouteMapper.selectOne(queryWrapper);
                return entity == null ? Optional.empty() : Optional.of(entity);
            });
        } catch (Exception e) {
            log.warn("getOne for [" + uniqueKey + "]. error", e);
            return null;
        }
    }

    /**
     * 分页列表
     */
    @Override
    public IPage<GatewayRouteEntity> page(IPage<GatewayRouteEntity> page, Wrapper<GatewayRouteEntity> queryWrapper) {
        return gatewayRouteMapper.selectPage(page, queryWrapper);
    }

    /**
     * 动态路由定义
     */
    @Override
    public Map<String, RouteDefinition> routeDefinitions() {
        LambdaQueryWrapper<GatewayRouteEntity> queryWrapper = Wrappers.<GatewayRouteEntity>lambdaQuery()
                .eq(GatewayRouteEntity::getStatus, 1);
        List<GatewayRouteEntity> routes = gatewayRouteMapper.selectList(queryWrapper);
        Map<String, RouteDefinition> routeDefinitions = new LinkedHashMap<>();
        routes.stream().forEach(routeEntity -> {
            String routeId = String.valueOf(routeEntity.getRouteId());
            RouteDefinition routeDefinition = RouteTransform.toRouteDefinition(routeEntity);
            routeDefinitions.put(routeId, routeDefinition);
        });
        return routeDefinitions;
    }


    /**
     * 违反了唯一约束条件吗？
     */
    @Override
    public boolean uniqueCondition(GatewayRouteEntity entity) {
        if (entity == null) {
            return true;
        }
        LambdaQueryWrapper<GatewayRouteEntity> queryWrapper = Wrappers.<GatewayRouteEntity>lambdaQuery()
                .eq(GatewayRouteEntity::getRoutePath, entity.getRoutePath())
                .eq(GatewayRouteEntity::getRouteMethod, entity.getRouteMethod())
                .eq(GatewayRouteEntity::getRouteVersion, entity.getRouteVersion());
        Integer result = gatewayRouteMapper.selectCount(queryWrapper);
        return SqlHelper.retBool(result);
    }


    /**
     * 违反了不可删除条件吗？
     */
    @Override
    public boolean notDeleteCondition(Collection<Long> routeIds) {
        if (CollectionUtils.isEmpty(routeIds)) {
            return false;
        }
        LambdaQueryWrapper<GatewayRouteEntity> queryWrapper = Wrappers.<GatewayRouteEntity>lambdaQuery()
                .in(GatewayRouteEntity::getRouteId, routeIds)
                .eq(GatewayRouteEntity::getStatus, 1);
        Integer result = gatewayRouteMapper.selectCount(queryWrapper);
        return SqlHelper.retBool(result);
    }
}
